package com.apobates.forum.toro.tuple.strategy;

import com.apobates.forum.toro.utils.BeanHelper;
import com.apobates.forum.toro.utils.CommonHelper;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * ASF Tuple聚合的公用方法
 * @author xiaofanku@live.cn
 * @since 20220517
 */
public abstract class AbstractTupleStrategy {
    private final static Logger logger = LoggerFactory.getLogger(AbstractTupleStrategy.class);
    /**
     * 标识子类的名称
     * @return
     */
    protected abstract String getSentinel();
    /**
     * 获取指定对象的属性值
     * @param baseObj 获取的目标对象
     * @param attrName 获取的属性名称
     * @param attrType 获取的属性类型
     * @param sentinel 哨兵,不参与实际功能。只作标识用
     * @return
     * @param <T>
     */
    protected <T> Object getBaseSelectVal(T baseObj, String attrName, String attrType, String sentinel) {
        Object data = null;
        try {
            data = BeanHelper.getAttrVal(baseObj, attrName, attrType);
        } catch (NullPointerException ex) {
            logger.debug(String.format("[TS][%s]%s对象不可用", getSentinel(), baseObj.getClass().getSimpleName()));
        } catch (Exception e) {
            logger.debug(String.format("[TS][%s]使用%s对象获取%s对象的查询参数失败", getSentinel(), baseObj.getClass().getSimpleName(), sentinel));
        }
        return data;
    }

    /**
     * 将左对象集合和右对象集合折叠为Collection<Pair<左对象,右对象>>
     * 注: 找到第一个满足条件的右对像后停止搜索其它
     * @param leftObjSet 左对象集合
     * @param rightObjSet 右对象集合
     * @param argumentMap 折叠时的连接参数
     * @return
     * @param <L>
     * @param <R>
     */
    protected <L,R> Collection<Pair<L,R>> foldPair(Collection<L> leftObjSet, Collection<R> rightObjSet, Map<String, String> argumentMap) {
        return leftObjSet.stream().map(leftObj->{
            Optional rightRsIns = findFirstEleOfCollection(
                    leftObj,
                    argumentMap.get("rightAttr"),
                    argumentMap.get("rightAttrType"),
                    argumentMap.get("joinAttr"),
                    rightObjSet,
                    "右");
            R rightObj = null;
            try {
                rightObj = (R)(rightRsIns.orElse(null));
            }catch (Exception e){
                logger.debug(String.format("[TS][%s]获取左对象对应的右对象时失败, 异常: %s", getSentinel(), e.getMessage()));
            }
            return Pair.of(leftObj, rightObj);
        }).collect(Collectors.toSet());
    }

    /**
     * 将左对象集合和右对象集合折叠为Collection<Pair<左对象,Collection<右对象>>>
     * 注: 返回所有满足条件的右对像
     * @param leftObjSet 左对象集合
     * @param rightObjSet 右对象集合
     * @param argumentMap 折叠时的连接参数
     * @return
     * @param <L>
     * @param <R>
     */
    protected <L,R> Collection<Pair<L,Collection<R>>> foldPairAny(Collection<L> leftObjSet, Collection<R> rightObjSet, Map<String, String> argumentMap) {
        return leftObjSet.stream().map(leftObj->{
            Collection<R> rightRsInsSet = findAnyEleOfCollection(
                    leftObj,
                    argumentMap.get("rightAttr"),
                    argumentMap.get("rightAttrType"),
                    argumentMap.get("joinAttr"),
                    rightObjSet,
                    "右");
            return Pair.of(leftObj, rightRsInsSet);
        }).collect(Collectors.toSet());
    }

    /**
     * 将左对象集合, 中对象集合和右对象集合折叠为Collection<Triple<左对象,中对象,右对象>>
     * @param leftObjSet 左对象集合
     * @param middleObjSet 中对象集合
     * @param rightObjSet 右对象集合
     * @param argumentMap 折叠时的连接参数
     * @return
     * @param <L>
     * @param <M>
     * @param <R>
     */
    protected <L,M,R> Collection<Triple<L,M,R>> foldTriple(Collection<L> leftObjSet, Collection<M> middleObjSet, Collection<R> rightObjSet, Map<String, String> argumentMap) {
        return leftObjSet.stream().map(leftObj->{
            // 中
            Optional middleRsIns = findFirstEleOfCollection(
                    leftObj,
                    argumentMap.get("middleAttr"),
                    argumentMap.get("middleAttrType"),
                    argumentMap.get("linkAttr"),
                    middleObjSet,
                    "中");
            M middleObj = null;
            try {
                middleObj = (M)(middleRsIns.orElse(null));
            }catch (Exception e){
                logger.debug(String.format("[TS][%s]获取中对象对应的右对象时失败, 异常: %s", getSentinel(), e.getMessage()));
            }
            // 右
            Optional rightRsIns = findFirstEleOfCollection(
                    leftObj,
                    argumentMap.get("rightAttr"),
                    argumentMap.get("rightAttrType"),
                    argumentMap.get("joinAttr"),
                    rightObjSet,
                    "右");
            R rightObj = null;
            try {
                rightObj = (R)(rightRsIns.orElse(null));
            }catch (Exception e){
                logger.debug(String.format("[TS][%s]获取左对象对应的右对象时失败, 异常: %s", getSentinel(), e.getMessage()));
            }
            return Triple.of(leftObj, middleObj, rightObj);
        }).collect(Collectors.toSet());
    }

    /**
     * 使用指定对象的某个属性作为查找目标，从待搜索的集合中搜索元素
     * @param targetObj 指定对象, 例:Orders
     * @param attrName 指定对象的属性名称, 例:productId
     * @param attrType 指定对象的属性类型, 例:long或java.lang.Long
     * @param linkAttrName 指定对象的属性关联集合元素的属性, 例:id(orders.productId = product.id)
     * @param srcObjSet 待搜索的集合, 例:Product集合
     * @param sentinel 哨兵,不参与实际功能。只作标识用
     * @return
     */
    protected Optional findFirstEleOfCollection(
            Object targetObj,
            String attrName,
            String attrType,
            String linkAttrName,
            Collection<?> srcObjSet,
            String sentinel) {
        Object expectVal = getBaseSelectVal(targetObj, attrName, attrType, sentinel);
        Optional chooseObj = CommonHelper.seekFirstEle(
                srcObjSet,
                linkAttrName,
                expectVal);
        return chooseObj;
    }

    /**
     * 使用指定对象的某个属性作为查找目标，从待搜索的集合中搜索元素
     * @param targetObj 指定对象, 例:Orders
     * @param attrName 指定对象的属性名称, 例:productId
     * @param attrType 指定对象的属性类型, 例:long或java.lang.Long
     * @param linkAttrName 指定对象的属性关联集合元素的属性, 例:id(orders.productId = product.id)
     * @param srcObjSet 待搜索的集合, 例:Product集合
     * @param sentinel 哨兵,不参与实际功能。只作标识用
     * @return
     * @param <T>
     */
    protected <T> Collection<T> findAnyEleOfCollection(
            Object targetObj,
            String attrName,
            String attrType,
            String linkAttrName,
            Collection<T> srcObjSet,
            String sentinel) {
        Object expectVal = getBaseSelectVal(targetObj, attrName, attrType, sentinel);
        return CommonHelper.seekAnyEle(
                srcObjSet,
                linkAttrName,
                expectVal);
    }
}
